﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;

namespace XemlUtilities.ISA.Ontology
{
    // base class : a mix between a factory (static methods) and a base class.

    public class OntologyBase
    {
        #region members

        public String Name { get; set; }
        public String URL { get; set; } // term URL
        public String OntologyURL { get; set; }
        private String _acronym;
        public String Acronym
        {
            //get;
            //set;
            get {
                if (_acronym == "XEO_Positioning") // TODO remove this as soon as we renamed all uses of XEO_Postioning to XPO
                {
                    return "XPO";
                }
                return _acronym;
            }
            set {
                _acronym = value;
            }
        }
        public String Version { get; set; }
        private List<Term> Terms { get; set; }

        // used to keep tack of all instantiated ontologies
        static Dictionary<String, OntologyBase> Ontologies { get; set; }
        public static String ConnectionString { get; set; }
        static List<MetaDataStruct> OntologyMetadata { get; set; }
        struct MetaDataStruct {
            public String Name;
            public Int32 BioPortalId;
            public String Version;
            public String URL;
            public String OntologyURL;
            public String Acronym;
        }

        #endregion members

        #region constructors
        public OntologyBase() {
            Terms = new List<Term>();
        }

        public OntologyBase(String acronym): base()
        {
            this.Acronym = acronym;
            this.Register();
        }

        // Make this into a OntologyFactory 
        static OntologyBase()
        {
            Ontologies = new Dictionary<String, OntologyBase>();
        }
        #endregion constructors

        // has to be called at the end of derived class' constructor
        // TODO decide weither to key on name or on acronym
        public void Register()
        {
            this.FillMissingData();
            Ontologies[this.Acronym] = this;
        }

        public void FillMissingData()
        {
            List<MetaDataStruct> metaData = GetMetaData();

            if (this.Acronym == null)
            {
                this.Acronym = metaData.Where(x => x.Name == this.Name).Select(x => x.Acronym).Single().ToString();
            }

            if (this.Name == null)
            {
                this.Name = metaData.Where(x => x.Acronym == this.Acronym).Select(x => x.Name).Single().ToString();
            }

            if (this.URL == null)
            {
                this.URL = metaData.Where(x => x.Acronym == this.Acronym || x.Name == this.Name).Select(x => x.URL).Single().ToString();
            }

            if (this.OntologyURL == null)
            {
                this.OntologyURL = metaData.Where(x => x.Acronym == this.Acronym || x.Name == this.Name).Select(x => x.OntologyURL).Single().ToString();
            }

            if (this.Version == null)
            {
                this.Version = metaData.Where(x => x.Acronym == this.Acronym || x.Name == this.Name).Select(x => x.Version).Single().ToString();
            }
        }

        private static List<MetaDataStruct> GetMetaData() {
            if (OntologyMetadata != null)
            {
                return OntologyMetadata;
            }
            List<MetaDataStruct> res = new List<MetaDataStruct>();
            using (SqlCommand cmd = new SqlCommand())
            using (cmd.Connection = new SqlConnection(ConnectionString))
            {
                try
                {
                    cmd.CommandType = System.Data.CommandType.Text;
                    cmd.CommandText = @"SELECT [name], [bioportalId], [version], [url], [acronym], [ontology_url] FROM [GMD].[obo].[Ontology]";
                    cmd.Connection.Open();
                    cmd.Transaction = cmd.Connection.BeginTransaction(IsolationLevel.ReadUncommitted);
                    using (SqlDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
                    {
                        while (reader.Read())
                        {
                            MetaDataStruct metaData;
                            metaData.Name = reader.GetString(0);
                            metaData.BioPortalId = reader.GetInt32(1);
                            metaData.Version = reader.GetString(2);
                            metaData.URL = reader.GetString(3);
                            metaData.Acronym = reader.GetString(4);
                            metaData.OntologyURL = reader.SafeGetString(5);
                            res.Add(metaData);
                        }
                    }
                }
                finally
                {
                    cmd.Connection.Close();
                }
            }
            OntologyMetadata = res;
            return res;
        }

        // creates ontology if not exists.
        public static OntologyBase GetOrCreateOntology(String acronym)
        {
            if ( ! Ontologies.ContainsKey(acronym))
            {
                OntologyBase ontology = new OntologyBase();
                ontology.Acronym = acronym;
                ontology.Register();
            }

            if (acronym == "XEO_Positioning")
            {
                return Ontologies["XPO"];
            }
            return Ontologies[acronym];
        }

        public static OntologyBase GetOrCreateOntology(String acronym, String termName) {
            OntologyBase ontology = OntologyBase.GetOrCreateOntology(acronym);

            // add the term
            Term term = new Term(ontology);
            term.Name = termName;
            ontology.AddTerm(term);

            return ontology;
        }

        public static Term GetOrCreateTerm(string ontologyAcronym, string termAccessionNr)
        {
            OntologyBase ontology = OntologyBase.GetOrCreateOntology(ontologyAcronym);
            
            // add the term
            Term term = new Term(ontology);
            term.AccessionNr = termAccessionNr;
            ontology.AddTerm(term);

            return term;
        }

        public static Term GetOrCreateTerm(string ontologyAcronym, string termAccessionNr, string termName)
        {
            Term term = GetOrCreateTerm(ontologyAcronym, termAccessionNr);
            term.Name = termName;

            return term;
        }

        public static Term GetOrCreateTermName(string acronym, string termName)
        {
            OntologyBase ontology = OntologyBase.GetOrCreateOntology(acronym);
            
            // add the term
            Term term = new Term(ontology);
            term.Name = termName;
            ontology.AddTerm(term);

            return term;
        }

        //public static Term GetOrCreateTermAccessionName(string acronym, string accessionNr, string termName)
        //{
        //    Term term = GetOrCreateTermName(acronym, termName);
        //    term.AccessionNr = accessionNr;

        //    return term;
        //}

        public static IEnumerable<OntologyBase> GetOntologies()
        {
            return Ontologies.Select(x => x.Value).ToList();
        }

        #region instance
        public virtual IEnumerable<Term> GetTerms()
        {
            return this.Terms;
        }

        public virtual void AddTerm(Term term)
        {
            this.Terms.Add(term);
        }
        #endregion instance
    }
}
